package com.suo.spring;

import com.sun.org.apache.xml.internal.utils.URI;
import com.suo.spring.annotation.Autowired;
import com.suo.spring.annotation.Component;
import com.suo.spring.annotation.ComponentScan;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ApplicationContext {

    // 定义一个线程安全的类信息池
    private Map<String, Class<?>> classPool = new ConcurrentHashMap<>();
    // 定义一个线程安全的类对象池
    private Map<String, Object> beanPool = new ConcurrentHashMap<>();

    public ApplicationContext(Class<?> configClass) {
        // 1.扫描配置信息中指定包下的类
        this.scan(configClass);
        // 2.实例化扫描到的类
        this.instantiateBean();
    }



    // 1.扫描配置信息中指定包下的类
    private void scan(Class<?> configClass) {
        // 解析配置类，获取到扫描包路径
        String basePackages = this.getBasePackages(configClass);
        // 使用扫描包路径进行文件遍历操作
        this.doScan(basePackages);
    }

    // 解析配置类，获取到扫描包路径
    private String getBasePackages(Class<?> configClass) {
        // 获取这个类对象中的注解对象
        ComponentScan componentScan = configClass.getAnnotation(ComponentScan.class);
        // 调用注解对象的方法获取属性值
        String basePackages = componentScan.basePackages();
        return basePackages;
    }

    // 使用扫描包路径进行文件遍历操作
    private void doScan(String basePackages) {
        // 将.转化为/
        String[] split = basePackages.split("\\.");
        String filePath = System.getProperty("user.dir") + "/src/" +  split[0];
        for (int i = 1; i < split.length; i++) {
            filePath += "/" + split[i];
        }


        // 获取资源信息
        File dir = new File(filePath);
        for (File file : dir.listFiles()) {
            if (file.isDirectory()) {
                // 递归扫描
                doScan(basePackages + "." + file.getName());
            }
            else {
                // com.my.spring.example + . + Boy.class -> com.my.spring.example.Boy
                String className = basePackages + "." + file.getName().replace(".java", "");
                // 将class存放到classMap中
                this.registerClass(className);
            }
        }
    }

    // 如果该类含有Component注解那么就将其加入到classMap中
    private void registerClass(String className){
        // 使用类加载器通过类名加载类信息
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        try {
            Class<?> clazz = classLoader.loadClass(className);
            // 获取这个类的Component注解
            Component component = clazz.getAnnotation(Component.class);
            // 如果存在Component注解则将其放入类信息池中
            if (component != null) {
                classPool.put(className,clazz);
            }
        } catch (ClassNotFoundException e) {
            System.out.println("类加载失败");
            e.printStackTrace();
        }
    }

    // 2. 实例化扫描到的类
    private void instantiateBean() {
        // 将classPool中的所有值都实例化，并获取
        for (Class<?> clazz : classPool.values()) {
            getBean(clazz);
        }
    }

    // 获取bean对象
    private Object getBean(Class<?> clazz) {
        // 先从缓存中获取
        Object bean = beanPool.get(clazz.getName());
        if (bean != null) return bean;
        // 如果没有则创建
        return createBean(clazz);
    }

    // 创建bean对象
    private Object createBean(Class<?> clazz) {
        // 1. 实例化bean
        Object bean = this.newInstance(clazz);
        // 2. 填充字段，将含有Autowired注解的字段注入bean对象
        try {
            this.populateBean(bean, clazz);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        // 3. 实例好并且注入完的bean放入beanPool中
        beanPool.put(clazz.getName(),bean);
        return bean;
    }

    // 实例化bean
    private Object newInstance(Class<?> clazz) {
        Object bean = null;
        try {
            bean = clazz.getDeclaredConstructor().newInstance();
        } catch (InstantiationException e) {
            System.out.println("bean实例化失败");
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            System.out.println("bean实例化失败");
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            System.out.println("bean实例化失败");
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            System.out.println("bean实例化失败");
            e.printStackTrace();
        }
        return bean;
    }

    // 填充字段，将含有Autowired注解的字段注入bean对象
    private void populateBean(Object bean, Class<?> clazz) throws IllegalAccessException {
        // 解析class信息，判断类中是否有需要进行依赖注入的字段
        Field[] fields = clazz.getDeclaredFields();
        // 遍历每一个属性
        for (Field field : fields) {
            // 获取属性的Autowired注解
            Autowired autowired = field.getAnnotation(Autowired.class);
            // 如果有Autowired这个注解
            if (autowired != null) {
                // 解析这个属性对应的类
                Object value = resolveBean(field.getType());
                // 爆破，因为属性可能是private
                field.setAccessible(true);
                // 将value值保存到bean对象的属性中
                field.set(bean,value);
            }
        }
    }

    // 解析bean用来实现声明接口时注入其子类对象
    private Object resolveBean(Class<?> clazz){
        // 判断clazz是不是一个接口
        if (clazz.isInterface()) {
            // 遍历classPool
            for (Map.Entry<String,Class<?>> entry : classPool.entrySet()) {
                // 如果clazz对应的类是entry对应的类的父类或接口
                if (clazz.isAssignableFrom(entry.getValue())) {
                    return getBean(entry.getValue());
                }
            }
            throw new RuntimeException("找不到可以注入的bean");
        }
        else {
            return getBean(clazz);
        }
    }
}


